/*
 * Copyright 2014 Jonathan Haas
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */

package app.grinreader.model;

import android.util.Log;
import android.util.Xml;
import nl.siegmann.epublib.domain.Book;
import nl.siegmann.epublib.domain.Resource;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;


public class ResourceData {

    private static final String ns = null;

    List<TextBlock> entries = new ArrayList<TextBlock>();
	private Book book;
	private int resourceId;
	private Styles styles = new Styles();

	public ResourceData(Book book, Resource resource, int resourceId) {
		this.book = book;
		this.resourceId = resourceId;
        try {
            XmlPullParser parser = Xml.newPullParser();
            parser.setInput(resource.getReader());
            parser.nextTag();

            parser.require(XmlPullParser.START_TAG, ns, "html");
            while (parser.next() != XmlPullParser.END_TAG) {
                if (parser.getEventType() != XmlPullParser.START_TAG) {
                    continue;
                }
                String name = parser.getName();
                // Starts by looking for the entry tag
                if (name.equals("body")) {
                    readBlockElement(parser);
                } else {
                    skip(parser);
                }

            }

        }
        catch (Exception e){
            Log.e("reader", "error", e);
        }
    }

	private HtmlElementClass getHtmlElementClass(String name) {
		switch (name){
			case "address":
			case "article":
			case "aside":
			case "audio":
			case "blockquote":
			case "canvas":
			case "dd":
			case "div":
			case "dl":
			case "fieldset":
			case "figcaption":
			case "figure":
			case "footer":
			case "form":
			case "h1":
			case "h2":
			case "h3":
			case "h4":
			case "h5":
			case "h6":
			case "header":
			case "hgroup":
			case "hr":
			case "noscript":
			case "ol":
			case "output":
			case "p":
			case "pre":
			case "section":
			case "table":
			case "tfoot":
			case "ul":
			case "video":
				return HtmlElementClass.Block;
			case "br":
				return HtmlElementClass.LineBreak;
			case "img":
				return HtmlElementClass.Image;
		}
		return HtmlElementClass.Inline;
	}

    private void readBlockElement(XmlPullParser parser) throws XmlPullParserException, IOException {
        if (parser.getEventType() != XmlPullParser.START_TAG) {
            throw new IllegalStateException();
        }
	    ParagraphBuilder builder = new ParagraphBuilder(entries, resourceId);
        int depth = 1;
        while (depth != 0) {
	        switch (parser.next()) {
                case XmlPullParser.END_TAG:
                    depth--;
                    removeMarkup(builder, parser);
	                switch(getHtmlElementClass(parser.getName())){
		                case Block:
		                builder.finishParagraph();
	                }
                    break;
                case XmlPullParser.START_TAG:
	                switch (getHtmlElementClass(parser.getName())){
		                case Block:
		                    builder.finishParagraph();
			                break;
		                case LineBreak:
			                builder.appendLineBreak();
			                skip(parser);
			                continue;
		                case Image:
			                String src = parser.getAttributeValue(null, "src");
			                builder.appendImage(book.getResources().getByHref(src));
			                skip(parser);
			                continue;
		                case Ignorable:
			                skip(parser);
			                continue;
	                }
	                addMarkup(builder, parser);
	                depth++;
                    break;
                case XmlPullParser.TEXT:
	                String text = parser.getText();
	                builder.appendText(text);
            }
        }
    }

	private void addMarkup(ParagraphBuilder builder, XmlPullParser parser) {
		StyleElement element = styles.forHtml(parser);
		if(element != null){
			builder.addMarkupStack(element);
		}
	}

	private void removeMarkup(ParagraphBuilder builder, XmlPullParser parser) {
		StyleElement element = styles.forHtml(parser);
		if(element != null){
			builder.popMarkupStack();
		}
	}

	private void skip(XmlPullParser parser) throws XmlPullParserException, IOException {
        if (parser.getEventType() != XmlPullParser.START_TAG) {
            throw new IllegalStateException();
        }
        int depth = 1;
        while (depth != 0) {
            switch (parser.next()) {
                case XmlPullParser.END_TAG:
                    depth--;
                    break;
                case XmlPullParser.START_TAG:
                    depth++;
                    break;
            }
        }
    }

    public TextBlock getElement(int elementIndex) {
        if(elementIndex < 0 || elementIndex >= getElementCount()) return null;
        return entries.get(elementIndex);
    }

    public int getResourceId() {
        return resourceId;
    }

    public int getElementCount() {
        return entries.size();
    }

	private enum HtmlElementClass {
		Block,
		Inline,
		LineBreak,
		Image,
		Ignorable
	}
}
